// OgreNext3_ColoredCube.cpp
#include <OgreRoot.h>
#include <OgreSceneManager.h>
#include <OgreCamera.h>
#include <OgreWindow.h>
#include <OgreMeshManager2.h>
#include <OgreManualObject2.h>
#include <OgreItem.h>
#include <OgreSceneNode.h>
#include <Compositor/OgreCompositorManager2.h>
#include <OgreWindowEventUtilities.h>
#include <OgreHlms.h>
#include <OgreHlmsDatablock.h>
#include <OgreArchiveManager.h>
#include <Hlms/Unlit/OgreHlmsUnlit.h>
#include <iostream>

using namespace Ogre;

int main()
{
    Root root;

    root.loadPlugin("RenderSystem_GL3Plus_d", "", false);

    const RenderSystemList& renderSystems = root.getAvailableRenderers();
    if (renderSystems.empty())
        return -1;
    root.setRenderSystem(renderSystems[0]);

    root.initialise(false);

    NameValuePairList params;
    params["vsync"] = "true";

    Window* window = root.createRenderWindow(
        "Ogre-Next 3.0 Colored Cube",
        400, 300,
        false,// fullScreen
        &params// other args
    );

    std::cout << "Window created: " << window->getWidth() << "x" << window->getHeight() << "\n";

    SceneManager* sceneMgr = root.createSceneManager(SCENE_DYNAMIC, 1, "MainSceneMgr");

    Camera* cam = sceneMgr->createCamera("MainCam");
    cam->setAutoAspectRatio(true);
    cam->setNearClipDistance(0.1f);
    cam->setFarClipDistance(1000.0f);

    SceneNode* camNode = sceneMgr->getRootSceneNode()->createChildSceneNode();
    camNode->setPosition(0, 0, 200);

    // Compositor
    CompositorManager2* compMgr = root.getCompositorManager2();
    String workspaceName("MainWorkspace");
    if (!compMgr->hasWorkspaceDefinition(workspaceName))
        compMgr->createBasicWorkspaceDef(workspaceName, ColourValue(0.0f, 1.0f, 0.0f));

    compMgr->addWorkspace(sceneMgr, window->getTexture(), cam, workspaceName, true);

    const String MediaPath = "D:/OGRE-Next/ogre-next-master/build/install/Media/";
    // Load HLMS archives
    Archive* archiveCommonAny = ArchiveManager::getSingletonPtr()->load(MediaPath + "Hlms/Common/Any", "FileSystem", true);
    Archive* archiveCommonGLSL = ArchiveManager::getSingletonPtr()->load(MediaPath + "Hlms/Common/GLSL", "FileSystem", true);
    Archive* archiveUnlitAny = ArchiveManager::getSingletonPtr()->load(MediaPath + "Hlms/Unlit/Any", "FileSystem", true);
    Archive* archiveUnlitGLSL = ArchiveManager::getSingletonPtr()->load(MediaPath + "Hlms/Unlit/GLSL", "FileSystem", true);

    // Build library vector
    ArchiveVec library;
    if (archiveCommonAny)  library.push_back(archiveCommonAny);
    if (archiveUnlitAny)   library.push_back(archiveUnlitAny);
    if (archiveCommonGLSL) library.push_back(archiveCommonGLSL);
    if (archiveUnlitGLSL) library.push_back(archiveUnlitGLSL);

    if (!archiveUnlitGLSL || archiveUnlitGLSL->listFileInfo()->empty())
    {
        std::cerr << "HlmsUnlit GLSL archive missing files!" << std::endl;
        return -1;
    }

    // Create HlmsUnlit and register
    HlmsUnlit* hlmsUnlit = OGRE_NEW HlmsUnlit(archiveUnlitGLSL, &library);
    root.getHlmsManager()->registerHlms(hlmsUnlit);

    auto datablock = hlmsUnlit->createDatablock(
        "MyDatablock", "MyHumanReadableName",
        HlmsMacroblock(), HlmsBlendblock(),
        HlmsParamVec(), true);

    if (!datablock) {
        std::cerr << "datablock not initialized!" << std::endl;
        return -1;
    }

    ManualObject* cube = sceneMgr->createManualObject();
    cube->begin("MyDatablock", OT_TRIANGLE_LIST);

    const float h = 50.0f;
    size_t baseVert = 0;

    auto addFace = [&](Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3, ColourValue c)
    {
        cube->position(v0); cube->colour(c);
        cube->position(v1); cube->colour(c);
        cube->position(v2); cube->colour(c);
        cube->position(v3); cube->colour(c);

        cube->quad(baseVert, baseVert + 1, baseVert + 2, baseVert + 3);
        baseVert += 4;
    };

    addFace(Vector3(-h, -h, h), Vector3(h, -h, h), Vector3(h, h, h), Vector3(-h, h, h), ColourValue(1, 0, 0));
    addFace(Vector3(h, -h, -h), Vector3(-h, -h, -h), Vector3(-h, h, -h), Vector3(h, h, -h), ColourValue(0, 1, 0));
    addFace(Vector3(-h, -h, -h), Vector3(-h, -h, h), Vector3(-h, h, h), Vector3(-h, h, -h), ColourValue(0, 0, 1));
    addFace(Vector3(h, -h, h), Vector3(h, -h, -h), Vector3(h, h, -h), Vector3(h, h, h), ColourValue(1, 1, 0));
    addFace(Vector3(-h, h, h), Vector3(h, h, h), Vector3(h, h, -h), Vector3(-h, h, -h), ColourValue(1, 0, 1));
    addFace(Vector3(-h, -h, -h), Vector3(h, -h, -h), Vector3(h, -h, h), Vector3(-h, -h, h), ColourValue(0, 1, 1));

    cube->end();

    MeshPtr mesh = cube->convertToMesh("ColoredCubeMesh");
    Item* item = sceneMgr->createItem(mesh, SCENE_DYNAMIC);
    SceneNode* cubeNode = sceneMgr->getRootSceneNode()->createChildSceneNode();
    cubeNode->attachObject(item);

    std::cout << "Cube attached and ready\n";

    while (!window->isClosed())
    {
        WindowEventUtilities::messagePump();
        cubeNode->yaw(Degree(0.2f));
        root.renderOneFrame();
    }

    return 0;
}